using System;
using System.Messaging;
using System.Reflection;
using System.Timers;
using log4net;

namespace Patriq.Msmq.Router.Core
{
    public interface IRouteEngine : IDisposable
    {
        void Start();
        void Stop();
    }
    public class RouteEngine : IRouteEngine
    {
        private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private readonly IQueueReader queueReader;
        private readonly IQueueWriter queueWriter;
        private readonly IRouteDecider routeDecider;
        private readonly Timer timer;
        
        public RouteEngine(IQueueReader queueReader, IQueueWriter queueWriter, IRouteDecider routeDecider, double checkInterval)
        {
            this.queueReader = queueReader;
            this.queueWriter = queueWriter;
            this.routeDecider = routeDecider;
            timer = new Timer(checkInterval);
            timer.Elapsed += OnTimerTick;
        }

        public void Start()
        {
            timer.Start();
        }

        public void Stop()
        {
            timer.Stop();
        }

        private void OnTimerTick(object sender, ElapsedEventArgs e)
        {
            Logger.Debug("Entered OnTimerTick");
            timer.Stop();
            while (true)
            {
                using (MessageQueueTransaction trans = new MessageQueueTransaction())
                {
                    try
                    {
                        trans.Begin();
                        Message message;
                        if (!queueReader.ReadMessage(trans, out message))
                            break;
                        var writePath = routeDecider.GetDestQueuePathByMessage(message);
                        Logger.DebugFormat("About to route message to queue [{0}]", writePath);
                        queueWriter.WriteMessage(writePath, trans, message);
                        trans.Commit();
                    }
                    catch (MessageQueueException msmqException)
                    {
                        if (msmqException.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
                            Logger.Error("Failed to route message", msmqException);
                        trans.Abort();
                        break;
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("Failed to route message - not an msmq excpetion", ex);
                        trans.Abort();
                        break;
                    }
                }
            }
            timer.Start();
        }

        public void Dispose()
        {
            timer.Dispose();
            queueReader.Dispose();
        }
    }
}